package lazy.util;

import java.lang.reflect.*;
import java.util.*;
import javax.servlet.http.HttpServletRequest;

public class BeanUtils {
	public static List<Method> getSetter(Class<?> cls) {
		List<Method> l = new ArrayList<Method>();
		for(Method m:cls.getMethods()) {
			if(m.getName().startsWith("set") && m.getParameterTypes().length==1)
				l.add(m);
		}
		return l;
	}
	public static List<Method> getGetter(Class<?> cls) {
		List<Method> l = new ArrayList<Method>();
		for(Method m:cls.getMethods()) {
			if("getClass".equals(m.getName())) continue;
			if(m.getName().startsWith("get")) l.add(m);
		}
		return l;
	}
	public static <T> List<T> bindForList(HttpServletRequest request, Class<T> cls) {
		return bindForList(request.getParameterMap(), cls);
	}
	public static <T> List<T> bindForList(Map<String,String[]> parameters, Class<T> cls) {
		if(cls==null) throw new IllegalArgumentException("Class is null.");
		if(parameters==null) throw new IllegalArgumentException("Map<String,String[]> is null.");
		//--------------------------------------------------
		List<T> tList = new ArrayList<T>();
		//-------------先new出List<T>的最大數量
		List<Method> setterList = getSetter(cls);
		Class<?> type;
		String[] params;
		try {
			for(Method setter:setterList) {
				type = setter.getParameterTypes()[0];
				if(type.isArray()) continue;
				params = parameters.get(StringUtils.toProperty(setter.getName()));
				if(!StringUtils.hasText(params) || tList.size()>=params.length) continue;
				for(int i=tList.size();i<params.length;i++) {
					tList.add(cls.newInstance());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return tList;
		}
		//---------------填值至tList
		Object value;
		for(Method setter:setterList) {
			type = setter.getParameterTypes()[0];
			params = parameters.get(StringUtils.toProperty(setter.getName()));
			if(!StringUtils.hasText(params)) continue;
			if(type.isArray()) {
				try {
					type = type.getComponentType();
					value = Array.newInstance(type, params.length);
					for(int i=0;i<params.length;i++) {
						Array.set(value, i, parseValue(type, params[i]));
					}
					for(T t:tList)
						setter.invoke(t, value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else {
				if(params.length>1) {
					for(int i=0;i<tList.size();i++) {
						if(i>=params.length) break;
						try {
							setter.invoke(tList.get(i), parseValue(type, params[i]));
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				} else {	//params.length=1
					try {
						value = parseValue(type, params[0]);
						if(value==null) break;
						for(int i=0;i<tList.size();i++) {
							setter.invoke(tList.get(i), value);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}			
		}
		return tList;
	}
	private static Object parseValue(Class<?> type, String param) throws Exception {
		Object value = null;
		if(StringUtils.hasText(param)) {
			if(type.equals(String.class)) {
				value = param;
			} else if(type.equals(Date.class)) {
				value = DateUtils.castDate(param);
			} else {
				value = type.getMethod("valueOf", String.class).invoke(type, param);
			}
		}
		return value;
	}
	public static <T> T bind(HttpServletRequest request, Class<T> cls) throws InstantiationException, IllegalAccessException  {
		return bind((Map<String,String[]>)request.getParameterMap(), cls.newInstance());
	}
	public static <T> T bind(HttpServletRequest request, T t) {
		return bind((Map<String,String[]>)request.getParameterMap(), t);
	}
	public static <T> T bind(Map<String,String[]> parameters, T t) {
		if(t==null) throw new IllegalArgumentException("Object is null.");
		if(parameters==null) throw new IllegalArgumentException("Map<String,String[]> is null.");
		Class<?> type;
		String[] params;
		Object value;		
		for(Method method:getSetter(t.getClass())) {
			type = method.getParameterTypes()[0];
			params = parameters.get(StringUtils.toProperty(method.getName()));
			value = null;
			if(StringUtils.hasText(params)) {
				if(type.isArray()) {
					try {
						type = type.getComponentType();
						value = Array.newInstance(type, params.length);
						for(int i=0;i<params.length;i++) {
							Array.set(value, i, parseValue(type, params[i]));
						}
						method.invoke(t, value);
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else {
					try {
						value = parseValue(type, params[0]);
						if(value!=null) method.invoke(t, value);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		return t;
	}
	public static Set<String> getProperties(Class<?> beanClass) {
		Set<String> set = new LinkedHashSet<String>();
		for(Method m:beanClass.getMethods()) {
			String s = m.getName();
			if(!s.equals("getClass") && (s=StringUtils.toProperty(s))!=null) {
				set.add(s);
			}
		}
		return set;
	}
	public static Object getPropertyValue(Object bean, String property) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Object value = null;
		if(bean!=null && StringUtils.hasText(property)) {
			value = bean.getClass().getMethod(StringUtils.toGetter(property)).invoke(bean);
		}
		return value;
	}
	public static String toJson(Object bean) {
		StringBuilder s = new StringBuilder();
		if(bean!=null) {
			s.append("{");
			boolean isFirst = true;
			for(String property:getProperties(bean.getClass())) {
				if(!isFirst) {
					s.append(",");
				} else {
					isFirst = false;
				}
				s.append(property).append(":");
				try {
					Object value = getPropertyValue(bean, property);
					if(value!=null) {
						if(value instanceof Date) {
							s.append("new Date(").append(((Date)value).getTime()).append(")");
						} else if(value instanceof Integer || value instanceof Long || value instanceof Float || value instanceof Double) {
							s.append(value.toString());
						}	else {
							s.append("\"").append(value.toString()).append("\"");
						}
					} else {
						s.append("null");
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			s.append("}");
		} else {
			s.append("null");
		}		
		return s.toString();
	}

	public static <T> T newInstance(Class<T> cls) throws InstantiationException, IllegalAccessException {
		return cls==null?null:cls.newInstance();
	}
	/*private static boolean isPrimitiveType(Class<?> type) {
		return Integer.class.equals(type)	|| Integer.TYPE.equals(type)
			|| Double.class.equals(type)	|| Double.TYPE.equals(type)
			|| Long.class.equals(type)		|| Long.TYPE.equals(type)
			|| Boolean.class.equals(type)	|| Boolean.TYPE.equals(type)
			|| Float.class.equals(type)		|| Float.TYPE.equals(type)
			|| Character.class.equals(type)	|| Character.TYPE.equals(type)
			|| Byte.class.equals(type)		|| Byte.TYPE.equals(type)
			|| Short.class.equals(type)		|| Short.TYPE.equals(type);
	}*/
	
	private static Object convertValue(Class<?> type, Object input) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if(type==null || input==null) return null;
		if(type.equals(input.getClass())) return input;
		Object value = null;
		if(type.isArray()) {
			type = type.getComponentType();
			if(input.getClass().isArray()) {
				int length = Array.getLength(input);
				value = Array.newInstance(type, length);
				for(int i=0;i<length;i++) {
					Array.set(value, i, convertValue(type, Array.get(input, i)));
				}
			} else {
				value = Array.newInstance(type, 1);
				Array.set(value, 0, convertValue(type, input));
			}			
		} else {
			if(input.getClass().isArray()) input = Array.get(input, 0);
			if(input==null) return null;
			if(type.equals(String.class)) {
				value = String.valueOf(input.toString());
			} else {
				value = PrimitiveConverter.convert(type, input);
			}
		}
		return value;
	}
	public static void copy(Object src, Object dest) {
		if(src==null || dest==null) return;
		//---------------------------------------
		Map<String,Object> srcPropertyValueMap = new HashMap<String, Object>();
		String property = null;
		StringBuilder log = new StringBuilder();
		for(Method m:getGetter(src.getClass())) {
			try {
				property = StringUtils.toProperty(m.getName());
				srcPropertyValueMap.put(property, m.invoke(src));
			} catch (Exception e) {
				//e.printStackTrace();
				//System.out.println(e.getMessage());
				log.delete(0, log.length());
				log.append("BeanUtils.copy(").append(src.getClass().getName()).append(",").append(dest.getClass().getName()).append(")");
				log.append("略過property：").append(property);
				System.out.println(log);
			}
		}
		//---------------------------------------
		for(Method m:getSetter(dest.getClass())) {
			property = StringUtils.toProperty(m.getName());
			System.out.print(property);
			System.out.println(m.getParameterTypes()[0]);
			//
			//System.out.println(property);
			try {
				Object o = convertValue(m.getParameterTypes()[0], srcPropertyValueMap.get(property));
				if(o==null) continue;
				m.invoke(dest, o);
			} catch (Exception e) {
				e.printStackTrace();
				log.delete(0, log.length());
				log.append("BeanUtils.copy(").append(src.getClass().getName()).append(",").append(dest.getClass().getName()).append(")");
				log.append("設定property：").append(property).append("至目標物件的").append(property).append("失敗！");
				System.out.println(log);
			}
		}
	}
}